/* thread_incr_rwlock.c

   This program employs two POSIX threads that increment the same global
   variable, synchronizing their access using a read/write lock. As a
   consequence, updates are not lost. Compare with thread_incr.c,
   thread_incr_mutex.c, and thread_incr_spinlock.c.
*/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static volatile int glob = 0;
static pthread_rwlock_t rwlock;

static void errExitEN(int errn, char *msg) {
  fprintf(stderr, "%s: %s\n", msg, strerror(errn));
  exit(EXIT_FAILURE);
}

static void errExit(char *msg) {
  perror(msg);
  exit(EXIT_FAILURE);
}

static void *logThreadFunc(void *arg) {
  int loops = *((int *)arg);

  while (1) {
    printf("%d", glob);
    if (glob == loops * 2) {
      printf("thread: %ld exit\n", pthread_self());
      break;
    }
    sleep(1);
  }

  return NULL;
}

/* Loop 'arg' times incrementing 'glob' */
static void *threadFunc(void *arg) {
  int loops = *((int *)arg);
  int loc, s;

  for (int j = 0; j < loops; j++) {
    s = pthread_rwlock_wrlock(&rwlock);
    if (s != 0) errExitEN(s, "pthread_rwlock_wrlock");

    loc = glob;
    loc++;
    glob = loc;

    s = pthread_rwlock_unlock(&rwlock);
    if (s != 0) errExitEN(s, "pthread_rwlock_unlock");
  }

  return NULL;
}

int main(int argc, char *argv[]) {
  pthread_t t1, t2;
  int loops, s;

  loops = (argc > 1) ? strtol(argv[1], NULL, 10) : 10000000;

  s = pthread_rwlock_init(&rwlock, 0);
  if (s != 0) errExitEN(s, "pthread_rwlock_init");

  s = pthread_create(&t1, NULL, threadFunc, &loops);
  if (s != 0) errExitEN(s, "pthread_create");
  s = pthread_create(&t2, NULL, threadFunc, &loops);
  if (s != 0) errExitEN(s, "pthread_create");

  s = pthread_join(t1, NULL);
  if (s != 0) errExitEN(s, "pthread_join");
  s = pthread_join(t2, NULL);
  if (s != 0) errExitEN(s, "pthread_join");

  printf("glob = %d\n", glob);
  exit(EXIT_SUCCESS);
}